package com.myspring;

import com.myspring.annotation.Autowired;
import com.myspring.annotation.Component;
import com.myspring.annotation.Qualifier;
import com.myspring.annotation.Value;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * description:
 * author:zt
 * date:2021-09-06
 */
public class MyAnnotationConfigApplicationContext {

    private Map<String,Object> ioc = new HashMap<>();
    private List<String> beanNames = new ArrayList<>();

    public MyAnnotationConfigApplicationContext(String pack){
        //1.遍历参数 包名，找到目标类
        Set<BeanDefinition> beanDefinitions = getBeanDefinitions(pack);
        //创建对象
        createObject(beanDefinitions);
        //自动装载
        autowireObject(beanDefinitions);
    }

    /**
     * 根据beanName从ioc容器中获取该bean的实例化对象
     * @param beanName
     * @return
     */
    public Object getBean(String beanName){
        return ioc.get(beanName);
    }

    public Integer getBeanDefinitionCount(){
        return beanNames.size();
    }

    /**
     * 找到加了Autowired注解的属性，自动装配
     * @param beanDefinitions
     */
    public void autowireObject(Set<BeanDefinition> beanDefinitions){
        Iterator<BeanDefinition> iterator = beanDefinitions.iterator();
        while (iterator.hasNext()) {
            BeanDefinition beanDefinition = iterator.next();
            Class clazz = beanDefinition.getBeanClass();
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                Autowired annotation = declaredField.getAnnotation(Autowired.class);
                if(annotation!=null){
                    Qualifier qualifier = declaredField.getAnnotation(Qualifier.class);
                    Object object = getBean(beanDefinition.getBeanName());  //获取要注入bean的类
                    if(qualifier!=null){
                        //byName
                        try {
                            //如果加了qualifier注解，那么根据qualifier注解的值作为bean的名字获取bean
                            String beanName = qualifier.value();
                            Object bean = getBean(beanName);
                            String fieldName = declaredField.getName(); //属性名称
                            String methodName = "set"+fieldName.substring(0, 1).toUpperCase()+fieldName.substring(1);  //该属性的set方法名
                            Method method = clazz.getMethod(methodName, declaredField.getType());  //根据方法名和参数类型获取set方法
                            method.invoke(object, bean);
                        } catch (NoSuchMethodException e) {
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        }
                    }else{
                        //byType
                        try {
                            String fieldName = declaredField.getType().getSimpleName();  //获取该参数的类型名称
                            Object bean = getBean(fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1)); //根据类型获取bean，那么beanName就是类名+首字母小写
                            String methodName = "set"+fieldName;
                            Method method = clazz.getMethod(methodName, declaredField.getType());  //获取set方法
                            method.invoke(object,bean);
                        } catch (NoSuchMethodException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

    /**
     * 将beanDefinitions中的bean全部实例化，存入ioc容器中
     * @param beanDefinitions
     */
    public void createObject(Set<BeanDefinition> beanDefinitions){
        Iterator<BeanDefinition> iterator = beanDefinitions.iterator();
        while (iterator.hasNext()) {
            BeanDefinition beanDefinition = iterator.next();
            Class clazz = beanDefinition.getBeanClass();
            String beanName = beanDefinition.getBeanName();
            try {
                //创建对象
                Object object = clazz.getConstructor().newInstance();
                //完成属性的赋值
                //getDeclaredFields获取类中的属性
                Field[] declaredFields = clazz.getDeclaredFields();
                for (Field declaredField : declaredFields) {
                    Value valueAnnotation = declaredField.getAnnotation(Value.class);
                    if (valueAnnotation!=null){
                        String value = valueAnnotation.value();
                        String fieldName = declaredField.getName(); //属性名
                        //获取该属性的set方法和@Value注解的值 进行注入
                        String methodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                        Method method = clazz.getMethod(methodName, declaredField.getType());
                        //完成数据类型转换
                        Object val = null;
                        switch (declaredField.getType().getName()){
                            case "java.lang.Integer":
                                val = Integer.parseInt(value);
                                break;
                            case "java.lang.String":
                                val = value;
                                break;
                            case "java.lang.Float":
                                val = Float.parseFloat(value);
                                break;
                        }
                        method.invoke(object,val);
                    }
                }
                //存入缓存
                ioc.put(beanName,object);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 扫描指定包下的类，如果添加指定注解，
     * 那么将其（beanName类名，clazz对象）作为beanDefinition加入beanDefinitions集合中
     * @param pack
     * @return
     */
    public Set<BeanDefinition> getBeanDefinitions(String pack){
        //1.获取包下所有类
        //3.将这些类封装成BeanDefinition，装载到集合中
        Set<Class<?>> classes = MyTools.getClasses(pack);
        Iterator<Class<?>> iterator = classes.iterator();
        HashSet<BeanDefinition> beanDefinitions = new HashSet<>();
        while (iterator.hasNext()) {
            //2.遍历这些类，找到添加了注解的类
            Class<?> clazz = iterator.next();
            //如果该类加了component注解，得到的componentAnnotation为一个component
            //如果没有加，得到的为null
            Component componentAnnotation = clazz.getAnnotation(Component.class);
//            System.out.println(componentAnnotation);
            if (componentAnnotation!=null){
                //获取Component注解的值
                String beanName = componentAnnotation.value();
                if ("".equals(beanName)){
                    //component注解没有加value，则以类名首字母小写作为beanName
                    //clazz.getName() 获取的是全路径名称，需要截取掉包名
                    String clazzName = clazz.getName().replaceAll(clazz.getPackage().getName() + ".", "");
                    beanName = clazzName.substring(0,1).toLowerCase()+clazzName.substring(1);
                }
                BeanDefinition beanDefinition = new BeanDefinition(beanName, clazz);
                beanDefinitions.add(beanDefinition);
                beanNames.add(beanName);
            }
        }
        return beanDefinitions;
    }
}
